package ai;

import util.ArrayList;
import util.List;

public class FuzzyEngine {

	List variables;
	List rules;

	public FuzzyEngine() {
		this.variables = new ArrayList();
		this.rules = new ArrayList();
	}

	public void addVariable(FuzzyVariable variable) {
		this.variables.add(variable);
	}

	public void addRule(Rule rule) {
		this.rules.add(rule);
	}

	public short defuzzify(List entries) {
		double sum = 0;
		double weights = 0;
		if (entries.size() != variables.size())
			return -1;
		for (byte i = 0; i < entries.size(); i++) {
			((FuzzyVariable) variables.get(i)).reset();
			((FuzzyVariable) variables.get(i))
					.fuzzify(((Double) entries.get(i)).doubleValue());
		}
		for (byte i = 0; i < rules.size(); i++) {
			((Rule) rules.get(i)).reset();
			((Rule) rules.get(i)).applyRule();
			sum += ((Rule) rules.get(i)).activationDegree
					* ((Rule) rules.get(i)).defuzzify();
			weights += ((Rule) rules.get(i)).activationDegree;
		}
		return (short) (sum / weights);
	}

	public class Rule {
		MemberShipFunction in;
		MemberShipFunction out;
		double activationDegree = 0;

		public Rule(MemberShipFunction in, MemberShipFunction out) {
			this.in = in;
			this.out = out;
		}

		public void applyRule() {
			this.activationDegree = in.activationDegree;
		}

		public void reset() {
			this.activationDegree = 0;
		}

		public double defuzzify() {
			return out.defuzzify;
		}
	}

	public class FuzzyVariable {

		String name;
		List memberShipFunctions;

		public FuzzyVariable(String name) {
			this.name = name;
			this.memberShipFunctions = new ArrayList();
		}

		public void addMemberShipFunction(MemberShipFunction function) {
			this.memberShipFunctions.add(function);
		}

		public void reset() {
			for (byte i = 0; i < this.memberShipFunctions.size(); i++) {
				((MemberShipFunction) this.memberShipFunctions.get(i)).reset();
			}
		}

		public void fuzzify(double X) {
			for (byte i = 0; i < this.memberShipFunctions.size(); i++) {
				((MemberShipFunction) this.memberShipFunctions.get(i))
						.fuzzify(X);
			}

		}

	}

	public class MemberShipFunction {

		String name;
		short[] range = new short[4];
		double activationDegree = 0;
		double defuzzify = 0;

		public MemberShipFunction(String name, double defuzzify) {
			this.name = name;
			this.defuzzify = defuzzify;
		}

		public MemberShipFunction(String name, short x1, short x2, short x3,
				short x4) {
			this.name = name;
			this.range[0] = x1;
			this.range[1] = x2;
			this.range[2] = x3;
			this.range[3] = x4;
		}

		public void fuzzify(double X) {
			// Check if input value is in range, if not, return 0
			// Determine which of 3 /-\ slopes works
			// For middle part, return 1
			if (X < range[0] || X > range[3]) {
				activationDegree = 0;
			} else if (X >= range[1] && X <= range[2]) {
				activationDegree = 1;
			} else if (X >= range[0] && X < range[1]) {
				activationDegree = (X - range[0]) / (range[1] - range[0]);
			} else if (X > range[2] && X <= range[3]) {
				activationDegree = (range[3] - X) / (range[3] - range[2]);
			} else {
				activationDegree = 0;
			}
		}

		public void reset() {
			this.activationDegree = 0;
		}

	}

}
